﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;

namespace JudgeKernel
{
    public class Judger
    {
        Dictionary<int, Lang> langs = new Dictionary<int, Lang>();
        KeyValuePair<string, string>? certify;
        string safePath;

        public Judger(string safepath, List<Lang> langs, KeyValuePair<string, string>? certify)
        {
            this.safePath = safepath;
            foreach (var lang in langs)
                this.langs.Add(lang.id, lang);
            this.certify = certify;

            foreach (var lang in langs.Where(l => l.standardTest))
            {
                int timesum = 0, memsum = 0, times = 0;
                for (int i = 0; i < 3; ++i)
                {
                    var spcCode = new Code
                    {
                        code = lang.standardCode,
                        lang = lang.id,
                        inputData = "",
                        outputaData = "",
                        timeLimit = 7200000,
                        memoryLimit = 268435456
                    };
                    var result = Judge(spcCode, EmptyFunc);
                    if (result.status != 3) continue;
                    timesum += result.exeTime.Value;
                    memsum += result.exeMem.Value;
                    ++times;
                }
                lang.timeWeight = (double)timesum / times / lang.standardTime;
                lang.memOffset = memsum / times - (lang.standardMem << 10);
            }
        }

        public Result Judge(Code code, Action<int> Response)
        {
            var result = new Result();
            if (!langs.ContainsKey(code.lang))
            {
                result.status = 11;
                return result;
            }
            var lang = langs[code.lang];
            var guid = Guid.NewGuid().ToString("N");
            Response(1); //Compiling
            var compiler = new CompileProcessor
            {
                guid = guid,
                code = code.code,
                safePath = safePath,
                compilePath = lang.compilerPath,
                compileParam = lang.compilerParam,
                codePath = lang.codePath,
                exePath = lang.exePath,
                compileErrorFilter = lang.ceInfoRegExp
            };
            var exepath = compiler.Compile();
            if (exepath == null)
            {
                result.status = 10; //CE
                result.ceInfo = compiler.compileErrorInfo;
                return result;
            }
            Response(2); //Judging

            code.inputData = toDOS(code.inputData + (char)26);
            code.outputaData = toDOS(code.outputaData);

            var runPath = lang.executePath.Replace("{safepath}", safePath)
                                          .Replace("{guid}", guid);
            string runParams = string.IsNullOrEmpty(lang.executeParam) ?
                null :
                runParams = lang.executeParam.Replace("{safepath}", safePath)
                                             .Replace("{guid}", guid);
            var judger = new JudgeProcessor
            {
                ExePath = runPath,
                ExeParam = runParams,
                InputData = code.inputData,
                TimeLimit = (int)(code.timeLimit * (lang.timeWeight ?? 1)),
                MemoryLimit = Math.Max(0, (code.memoryLimit << 10) + (lang.memOffset ?? 0)),
                OutputLimit = code.outputaData.Length > 524288 ? code.outputaData.Length * 2 : 524288,
                Certify = certify
            };
            var isJudge = judger.Judge();

            for (int i = 0; i < 3; ++i)
            {
                try
                {
                    File.Delete(exepath);
                    break;
                } catch { }
            }
            if (!isJudge)
            {
                result.status = 11; //SE
                return result;
            }

            var resultOutput = toDOS(judger.OutputData);
            result.exeOutput = resultOutput;
            if ((int)judger.Status > 2)
            {
                result.status = (int)judger.Status;
                switch ((int)judger.Status)
                {
                    case 5: result.exeMem = (int)(judger.MemoryUsed >> 10); break; //TLE
                    case 6: result.exeTime = (int)judger.TimeUsed; break; //MLE
                    case 9: result.reCode = (int)judger.RuntimeErrorState; break; //RE
                    default: //OLE SE
                        result.exeTime = (int)judger.TimeUsed;
                        goto case 5;
                }
                return result;
            }

            if (!code.specialJudge)
            {
                result.exeTime = (int)judger.TimeUsed;
                result.exeMem = (int)(judger.MemoryUsed >> 10);
                if (resultOutput == code.outputaData)
                    result.status = 3; //AC
                else if (Regex.Replace(resultOutput, @"\s", "") == Regex.Replace(code.outputaData, @"\s", ""))
                    result.status = 4; //PE
                else
                    result.status = 8; //WA
            }
            else
            {
                var spcCode = new Code
                {
                    code = code.specialCode,
                    lang = code.specialLang.Value,
                    inputData = resultOutput,
                    outputaData = code.outputaData,
                    timeLimit = 7200000,
                    memoryLimit = 268435456
                };
                var spcResult = Judge(code, EmptyFunc);
                result.status = spcResult.status;
                result.reCode = spcResult.reCode;
                result.specialOutput = spcResult.exeOutput;
            }
            return result;
        }

        void EmptyFunc(int i) { }

        static string toDOS(string str)
        {
            if (string.IsNullOrEmpty(str)) return "";
            return str.Replace("\r\n", "\n").Replace("\n", "\r\n");
        }
    }
}
